到数组的指针
在C++里,指针和数组密切相关。一个数组的名字能够被用做到它的开始元素的指针。例如,
int v[] = { 1, 2, 3, 4 };
int* p1 = v; // 指向开始元素(隐式转换)
int* p2 = &v[0]; // 指向开始元素
int* p3 = &v[4]; // 指向最后元素之后一个位置
取得超出一个数组结束之后一个元素位置的指针,这件事将保证可以做到。这一点在许多算法中都非常重要(2.7.2节、18.3节)。当然,这个指针事实上并不指向数组里的一个元素,因此不能通过它去读或者写。取得数组开始元素之前的元素地址是无定义的,应该避免。在某些机器结构中,数组常常被分配在机器地址的边界上,所以“开始元素之前的一个位置”根本就没有意义。
从数组名到这个数组的开始元素的指针的隐式转换,在C风格代码的函数调用中广泛使用。例如,
extern "C" int strlen(const char*); // 来自<string.h>
void f()
{
char v[] = "Annemarie";
char* p = v; // 隐式地从char[]转换到char*
strlen(p);
strlen(v); // 隐式地从char[]转换到char*
v = p; // 错误❌:不能给数组赋值
}
在两个调用中,传给标准库函数strlen()将是同一个值。困难在于无法避免这种隐式的转换。换句话说,没办法去定义一个函数,使得在函数调用时能够将整个数组复制给它。幸好并不存在从指针到数组的隐式转换。
数组参数被隐式地转换到指针,这也意味着对于被调用函数而言,数组的大小就会被丢掉。这就要求被调函数必须以某种方式去确定数组的大小,以便完成各种有意义的操作。与C标准库中其他以字符指针为参数的函数以牙膏,strlen()依靠0确定字符串结束。strlen(p)将返回知道表示结束位置的0(但不包括它)的字符个数。这是一类相当极低的东西。标准库vector(16.3节)和string(第20章)都不会遇到这种问题。
🔚